# -*- coding: utf-8 -*-

import logging
import threading
import time
from datetime import datetime
from functools import wraps

from sehnsucht.cron import Cron

logger = logging.getLogger(__name__)


def scheduler(cron, retry_times=3, retry_interval=5, excludes=(), includes=()):
    scheduler_cron = Cron(cron)
    execute_datetime = scheduler_cron.next_execute_time()

    def decorator(func):

        @wraps(func)
        def wrapper():
            try:
                next_interval = (next(execute_datetime) - datetime.now()).total_seconds()
                threading.Timer(interval=next_interval, function=wrapper).start()
            except StopIteration:
                logger.warning("there is no next-execution-datetime found")

            executed_times = 0
            while executed_times <= retry_times:
                try:
                    logger.debug("start to execute the task %s", func.__name__)
                    func()
                    logger.debug("finish to execute the task %s", func.__name__)
                except Exception as e:
                    logger.error("it is failure to execute the task %s, the error is %s", func.__name__, str(e))

                    is_exclude = False
                    for exclude in excludes:
                        if isinstance(e, exclude):
                            is_exclude = True
                    if is_exclude:
                        raise e

                    is_include = False
                    for include in includes:
                        if isinstance(e, include):
                            is_include = True

                    if is_include or len(includes) == 0:
                        if executed_times < retry_times:
                            time.sleep(retry_interval)
                        executed_times += 1
                    else:
                        raise e

                    if executed_times > retry_times:
                        raise e
                else:
                    break

        try:
            next_execution_datetime = next(execute_datetime)
            while not (next_execution_datetime - datetime.now()).total_seconds() > 0:
                next_execution_datetime = next(execute_datetime)
            interval = (next_execution_datetime - datetime.now()).total_seconds()
            threading.Timer(interval=interval, function=wrapper).start()
            return wrapper
        except StopIteration:
            logger.warning("there is no next-execution-datetime found")

    return decorator


def add_scheduler_task(func, cron, retry_times=3, retry_interval=5, excludes=(), includes=()):
    scheduler(cron, retry_times, retry_interval, excludes, includes)(func)
